﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;

namespace Xceed.Silverlight.Data
{
  internal abstract class DataRangeEnumeratorBase : IDataRangeEnumerator
  {
    #region Static Fields

    private static readonly Guid GetNextCallId = DataUtils.CreateCallId();

    #endregion


    protected DataRangeEnumeratorBase()
    {
    }


    #region IsGettingNext Property

    public bool IsGettingNext
    {
      get
      {
        return m_isGettingNext;
      }
      private set
      {
        if( value != m_isGettingNext )
        {
          m_isGettingNext = value;

          if( !value )
          {
            this.OnGetNextCompleted( EventArgs.Empty );
          }
        }
      }
    }

    private bool m_isGettingNext; //false

    #endregion

    #region HasMore Property (Protected)

    protected virtual bool HasMore
    {
      get
      {
        return !m_disposed;
      }
    }

    #endregion


    #region GetNextCompleted Event

    public event EventHandler GetNextCompleted;

    private void OnGetNextCompleted( EventArgs e )
    {
      if( this.GetNextCompleted != null )
      {
        this.GetNextCompleted( this, e );
      }
    }

    #endregion


    public IAsyncResult BeginGetNext( AsyncCallback callback, object state )
    {
      this.CheckState();

      AsyncResult asyncResult;

      // We already know there is no more item.
      if( !this.HasMore )
      {
        asyncResult = new AsyncResult( DataRangeEnumeratorBase.GetNextCallId, callback, state );
        asyncResult.StartSync( new GetNextResult() );
      }
      else
      {
        // This anonymous method resets the IsGettingNext flag when the item is fetched.
        AsyncCallback resetFlag =
          ( IAsyncResult ar ) =>
          {
            // This call will raise the GetNextCompleted event
            this.IsGettingNext = false;

            // This call will execute the user callback 
            if( callback != null )
            {
              callback.Invoke( ar );
            }
          };

        asyncResult = new AsyncResult( DataRangeEnumeratorBase.GetNextCallId, resetFlag, state );

        try
        {
          this.IsGettingNext = true;

          asyncResult.Start(
            this.BeginGetNextCore,
            ( par, iar ) => new DependentCallResult( this.EndGetNextCore( iar ) ) );
        }
        catch( Exception )
        {
          this.IsGettingNext = false;
          throw;
        }
      }

      return asyncResult;
    }

    public GetNextResult EndGetNext( IAsyncResult asyncResult )
    {
      asyncResult.ValidateCallEnd( DataRangeEnumeratorBase.GetNextCallId );

      return asyncResult.GetCallResult<GetNextResult>();
    }

    public abstract void Reset();

    protected abstract IAsyncResult BeginGetNextCore( AsyncCallback callback, object state );
    protected abstract GetNextResult EndGetNextCore( IAsyncResult asyncResult );

    protected void CheckState()
    {
      this.CheckDisposed();
      this.CheckGetNext();
    }

    private void CheckDisposed()
    {
      if( m_disposed )
        throw new ObjectDisposedException( "The current enumerator has been disposed." );
    }

    private void CheckGetNext()
    {
      if( m_isGettingNext )
        throw new InvalidOperationException( "The enumerator is currently moving next." );
    }


    #region IDispose Members

    public void Dispose()
    {
      this.Dispose( true );
      GC.SuppressFinalize( this );
    }

    protected virtual void Dispose( bool disposing )
    {
      this.GetNextCompleted = null;

      m_disposed = true;
    }

    ~DataRangeEnumeratorBase()
    {
      this.Dispose( false );
    }

    private bool m_disposed; //false

    #endregion
  }
}
